package com.richfit.cuba.modular.util;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.math.BigDecimal;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.crypto.Cipher;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import com.google.common.base.Joiner;
import com.google.common.collect.Lists;

import cn.hutool.core.bean.BeanException;
import cn.hutool.core.bean.DynaBean;
import cn.hutool.core.collection.CollectionUtil;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class Certificates {

	public static final String SPLIE_CHAR="###";
	/**
	 * Obtain annotation
	 */
	public static String getTableName(Class<?> clazz)
			throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
		Annotation[] anns = clazz.getAnnotations();
		for (java.lang.annotation.Annotation ann : anns) {
			InvocationHandler invocationHandler = Proxy.getInvocationHandler(ann);
			Field declaredField = invocationHandler.getClass().getDeclaredField("memberValues");
			declaredField.setAccessible(true);
			Map memberValues = (Map) declaredField.get(invocationHandler);
			return (String) memberValues.get("value");
		}
		return "";
	}
	/**
	 * 
	 * @param cipherTexts密文
	 * @param user_cert证书字符串
	 * @param timeTemp明文
	 * @return
	 */
	public static String RSADecryptByPublicKey(String cipherTexts, String user_certs, String timeTemp) {
		PublicKey pubKey;
		CertificateFactory cf;
		byte[] cipherText;
		Cipher cipher;
		String decrypt;
		// Before importing the certificate, you need to add the header of the base64. The CertificateFactory generates the certificate object. The stream as a parameter must have a base64 file header, and the certificate generated by the UKeyCom.dll control does not contain the certificate.
		String user_cert = "-----BEGIN CERTIFICATE-----\r\n" + user_certs;
		user_cert = user_cert + "-----END CERTIFICATE-----";
		try {
			KeyFactory mykeyFactory = KeyFactory.getInstance("RSA");
			BASE64Decoder dec = new BASE64Decoder();
			cipherText = dec.decodeBuffer(cipherTexts);
			cf = CertificateFactory.getInstance("X.509");
			ByteArrayInputStream byteArrayInput = new ByteArrayInputStream(user_cert.getBytes());
			BufferedInputStream bufferedInput = new BufferedInputStream(byteArrayInput);
			X509Certificate oCert = null;
			if (bufferedInput.available() > 0) {
				oCert = (X509Certificate) cf.generateCertificate(bufferedInput);
			}
			cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
			pubKey = oCert.getPublicKey();

			// 开始解密
			cipher.init(Cipher.DECRYPT_MODE, pubKey);
			byte[] plainText = cipher.doFinal(cipherText);
			decrypt = new String(plainText);
		} catch (CertificateException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
		return decrypt;
	}

	// 加密
	public static String encryption(String cert, String input) {
		PublicKey pubKey;

		CertificateFactory cf;
		byte[] cipherText;
		Cipher cipher;
		String encryption;
		try {
			// Before importing the certificate, you need to add the base64 file header. The CertificateFactory generates the certificate object. The stream as a parameter must have a BASE64 file header, and the certificate generated by the UKeyCom.dll control does not contain the certificate.
			String user_cert = "-----BEGIN CERTIFICATE-----\r\n" + cert;
			user_cert = user_cert + "-----END CERTIFICATE-----";
			cf = CertificateFactory.getInstance("X.509");
			ByteArrayInputStream byteArrayInput = new ByteArrayInputStream(user_cert.getBytes());
			BufferedInputStream bufferedInput = new BufferedInputStream(byteArrayInput);
			X509Certificate oCert = null;
			if (bufferedInput.available() > 0) {
				oCert = (X509Certificate) cf.generateCertificate(bufferedInput);
			}
			pubKey = oCert.getPublicKey();
			cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
			cipher.init(Cipher.ENCRYPT_MODE, pubKey);
			cipherText = cipher.doFinal(input.getBytes());
			BASE64Encoder base64Encoder = new BASE64Encoder();
			// 加密后的东西
			encryption = base64Encoder.encode(cipherText);
		} catch (Exception e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
			return null;
		}
		return encryption;
	}

	// Get the private key
	public static PrivateKey getPrivateKey(String key) throws Exception {
		byte[] keyBytes;
		keyBytes = (new BASE64Decoder()).decodeBuffer(key);
		PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance("RSA");
		PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
		return privateKey;
	}

	// Get the public key
	public static PublicKey getPublicKey(String key) throws Exception {
		byte[] keyBytes;
		keyBytes = (new BASE64Decoder()).decodeBuffer(key);
		java.security.spec.X509EncodedKeySpec keySpec = new java.security.spec.X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance("RSA");
		PublicKey publicKey = keyFactory.generatePublic(keySpec);
		return publicKey;
	}

	// Decrypt
	public static String decrypt(String privateKsey, String cipherTexts) {
		if(StringUtils.isBlank(privateKsey) || StringUtils.isBlank(cipherTexts)) {
			return null;
		}
		PrivateKey privKey;
		// CertificateFactory cf;
		byte[] cipherText;
		Cipher cipher;
		String decrypt;
		try {

			BASE64Decoder dec = new BASE64Decoder();
			cipherText = dec.decodeBuffer(cipherTexts);
			cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
			privKey = getPrivateKey(privateKsey);
			// begin decrypt
			cipher.init(Cipher.DECRYPT_MODE, privKey);
			byte[] plainText = cipher.doFinal(cipherText);
			decrypt = new String(plainText);
		} catch (CertificateException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
		return decrypt;
	}

	/**
	 * *数据组合
	 * @param bean
	 * @param dataType
	 * @return
	 */
	public static String valueJoin(Object bean, String dataType) {
		if(StringUtils.isBlank(dataType)|| bean==null) {
			return null;
		}
		DynaBean beans = DynaBean.create(bean);
		String[] list = dataType.split(",");
		String encrypteds = null;
		List<String> joinStr = Lists.newArrayList();
		for (int i = 0; i < list.length; i++) {// 循环取值加密
			try {
				Object val = beans.get(list[i]);
				if(val == null) {
					val = "";
				}
				if (val instanceof Date) {
					SimpleDateFormat forma = new SimpleDateFormat("yyyy/MM/dd");
					joinStr.add(forma.format(val));
				} else if (val instanceof Double || val instanceof Integer
						|| val instanceof BigDecimal || val instanceof Float 
						|| val instanceof Short
						|| val instanceof Number) {
					joinStr.add(new BigDecimal(val.toString()).setScale(3, BigDecimal.ROUND_HALF_UP).stripTrailingZeros().toPlainString());
				} else {
					joinStr.add(val.toString());
				}
			} catch (BeanException e) {
				e.printStackTrace();
			}
		}
		if(CollectionUtil.isNotEmpty(joinStr)) {
			boolean flag = false;
			for (String str : joinStr) {
				if(StringUtils.isNotBlank(str)) {
					flag = true;
					break;
				}
			}
			if(flag) {
				encrypteds = Joiner.on(SPLIE_CHAR).join(joinStr);
			}
		}
		return encrypteds;
	}
	
	/**
	 * *剔除字符结尾的分隔符
	 * @param val
	 * @return
	 */
	public static String handleStringData(String val) {
		if(StringUtils.isBlank(val)) {
			return val;
		}
		int strLength = val.indexOf(Certificates.SPLIE_CHAR);
		if(strLength>-1) {
			strLength = val.length()-Certificates.SPLIE_CHAR.length();
			if(strLength >0 && val.substring(strLength).equals(Certificates.SPLIE_CHAR)) {
				val = val.substring(0, strLength);
			}
		}
		return val;
	}
	
	/**
	 * *比对2个字符串的值是否相等。
	 * @param oldVal 原始值，加密表中解析出来的值
	 * @param newVal 业务表值，业务表中的数据值
	 * @return
	 */
	public static boolean isEqualEncrypted(String oldVal, String newVal) {
		boolean flag = false;
		oldVal = handleStringData(oldVal);
		if(StringUtils.isBlank(oldVal) && StringUtils.isBlank(newVal)) {
			flag  = true;
		}else if(NumberUtils.isCreatable(oldVal) && NumberUtils.isCreatable(newVal)) {
			flag = (new BigDecimal(oldVal).compareTo(new BigDecimal(newVal)) == 0);
		} else {
			if(StringUtils.isNotBlank(oldVal)) {
				flag = oldVal.equals(newVal);
			}
		}
		return flag;
	}
	
	// Value encryption for inserted records
	public static String valueEncryption(Object bean, String dataType, String certificate) {
		// DynaBean is a wrapper class that dynamically manipulates JavaBeans using reflection. With this class, you can dynamically call the get and set methods by passing in the name of the string.
		String encrypted = valueJoin(bean, dataType);
		if(StringUtils.isNotBlank(encrypted)) {
			encrypted = encryption(certificate, encrypted);// encrypt 加密
//			String encrypt = Certificates.decrypt(user.getPrivatekey(), encrypted);
		}

		return encrypted;
	}

//==========================================================================================//
	/**
	 *  Verification signature
	 * 
	 * @param cert
	 * @param ciphertext
	 * @param plaintext
	 *            
	 * @return
	 * @throws AuthenticationException
	 */
//	public static boolean VerificationSignature(String cert, String ciphertext, String plaintext)
//			throws AuthenticationException {
//		CertificateFactory cf;
//		byte[] cipherText;
//		try {
//			PublicKey pub = getPublicKey(cert);
//			BASE64Decoder dec = new BASE64Decoder();
//			String signatureType = Global.getEncryConfig("SignatureType");
//			// 创建签名对象
//			Signature oSign = Signature.getInstance(signatureType);
//			// 初始化签名对象
//			oSign.initVerify(pub);
//			// 解码签名值
//			// 加密后的东西
//			cipherText = dec.decodeBuffer(ciphertext);
//
//			oSign.update(plaintext.getBytes());
//			// 验证数字签名
//			boolean verifyRet = oSign.verify(cipherText);
//			if (!verifyRet) {
//				throw new AuthenticationException("msg:签名不正确 , 请检查是否已经插入ukey.");
//			} else {
//				return true;
//			}
//
//		} catch (Exception e1) {
//			// TODO Auto-generated catch block
//			throw new AuthenticationException("msg:签名不正确 , 请检查是否已经插入ukey.");
//		}
//	}
	//==========================================================================================//
	/***
	 * reference： https://blog.csdn.net/down177/article/details/43703473
	 * https://blog.csdn.net/u010936475/article/details/52664600 
	 * Read the *.cer public key certificate file to obtain the public key certificate information.
	 * 
	 * @author xgh
	 */
	public static String testReadX509CerFile(File file) throws Exception {
		try {
			// 读取证书文件
			InputStream inStream = new FileInputStream(file);
			// 创建X509工厂类
			CertificateFactory cf = CertificateFactory.getInstance("X.509");
			// 创建证书对象
			X509Certificate oCert = (X509Certificate) cf.generateCertificate(inStream);
			inStream.close();
			BASE64Encoder base64Encoder = new BASE64Encoder();
			String publicKey = base64Encoder.encode(oCert.getPublicKey().getEncoded());
			return publicKey;
		} catch (Exception e) {

			e.printStackTrace();
			return null;

		}

	}

	/**
	 * Obtain a certificate
	 */
//	public static String readCertificate(File file) throws Exception {
//		KeyStore ks = KeyStore.getInstance("PKCS12");
//		FileInputStream fis = new FileInputStream(file);
//		char[] nPassword = null;
//		if ((Global.getEncryConfig("KEYSTORE_PASSWORD") == null)
//				|| Global.getEncryConfig("KEYSTORE_PASSWORD").equals("")) {
//			nPassword = null;
//		} else {
//			nPassword = Global.getEncryConfig("KEYSTORE_PASSWORD").toCharArray();
//		}
//		ks.load(fis, nPassword);
//		fis.close();
//		Enumeration enum1 = ks.aliases();
//		String keyAlias = null;
//		if (enum1.hasMoreElements()) // we are readin just one certificate.
//		{
//			keyAlias = (String) enum1.nextElement();
//		}
//		Certificate cert = ks.getCertificate(keyAlias);
//		PrivateKey prikey = (PrivateKey) ks.getKey(keyAlias, nPassword);
//		String certs = new sun.misc.BASE64Encoder().encode(cert.getEncoded());
//		String privatekey = new sun.misc.BASE64Encoder().encode(prikey.getEncoded());
//		JSONObject json = new JSONObject();
//		json.put("cert", certs);
//		json.put("privateKey", privatekey);
//		return json.toString();
//	}

}
